//==============================================================================================================================================================
// Author(s): Roman Khmelichek, wei jiang
//
//==============================================================================================================================================================

#include "index_cat.h"

#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <zlib.h>


#include "boost/algorithm/string.hpp"
#include "cache_manager.h"
#include "configuration.h"
#include "config_file_properties.h"
#include "document_collection.h"
#include "index_reader.h"
#include "index_util.h"
using namespace std;

//This is the IndexCat::IndexCat constructor.
IndexCat::IndexCat(const IndexFiles& index_files) :
  index_(NULL), includes_contexts_(true), includes_positions_(true) {
  CacheManager* cache_policy = new MergingCachePolicy(index_files.index_filename().c_str());
  IndexReader* index_reader = new IndexReader(IndexReader::kMerge, *cache_policy, index_files.lexicon_filename().c_str(),
                                              index_files.document_map_basic_filename().c_str(), index_files.document_map_extended_filename().c_str(),
                                              index_files.meta_info_filename().c_str(), true);

  if (!index_reader->includes_contexts())
    includes_contexts_ = false;
  if (!index_reader->includes_positions())
    includes_positions_ = false;

  index_ = new Index(cache_policy, index_reader);
}

// If 'term' is NULL and 'term_len' is 0, it will go through and output all the lists in the index into a readable format.
// If 'term' is not NULL and 'term_len' is greater than 0, it will find the corresponding list and output this list into a readable format.
void IndexCat::Cat(const char* term, int term_len) {
  assert((term == NULL && term_len == 0) || (term != NULL && term_len > 0));

  while (index_->NextTerm()) {
	if ((term == NULL && term_len == 0) || (index_->curr_term_len() == term_len && strncmp(index_->curr_term(), term, min(index_->curr_term_len(), term_len))
		== 0)) {
	  for (int i = 0; i < index_->curr_term_len(); ++i) {
		printf("%c", index_->curr_term()[i]);
	  }
	  printf(":\n");

	  while (index_->NextDocId()) {
		uint32_t curr_frequency = index_->curr_list_data()->GetFreq();
		printf("(%u, %u, <", index_->curr_doc_id(), curr_frequency);

		if (includes_positions_) {
		  const uint32_t* curr_positions = index_->curr_list_data()->curr_chunk_decoder().current_positions();
		  uint32_t num_positions = index_->curr_list_data()->GetNumDocProperties();
		  for (size_t i = 0; i < num_positions; ++i) {
			printf("%u", curr_positions[i]);
			if (i != (num_positions - 1))
			  printf(", ");
		  }
		}

		printf(">)\n");
	  }

	  printf("\n");
	}
  }

}

// If 'term' is NULL and 'term_len' is 0, it will go through and output all the lists in the index into a readable format.
// If 'term' is not NULL and 'term_len' is greater than 0, it will find the corresponding list and output this list into a readable format.(One term only)
void IndexCat::Cat(const char* term, int term_len, string flagString, ofstream &outputFileHandler, string queryNumber, string docID) {
  assert((term == NULL && term_len == 0) || (term != NULL && term_len > 0));
  int counter = 0;
  bool findFlag = false;
  if (flagString == "2"){

	  while (index_->NextTerm()) {
	    if ((term == NULL && term_len == 0) || (index_->curr_term_len() == term_len && strncmp(index_->curr_term(), term, min(index_->curr_term_len(), term_len))
	        == 0)) {
	      for (int i = 0; i < index_->curr_term_len(); ++i) {

	        outputFileHandler << index_->curr_term()[i];
	      }
	      outputFileHandler << " ";
	      outputFileHandler << queryNumber;
	      outputFileHandler << " ";

	      while (index_->NextDocId()) {
	    	//cout << "curr_doc_id:" << index_->curr_doc_id() << endl;
	    	if (index_->curr_doc_id() == strtoul(docID.c_str(),NULL,0 ) ){
		    	counter ++;
		        uint32_t curr_frequency = index_->curr_list_data()->GetFreq();

		        outputFileHandler << index_->curr_doc_id() << " " << curr_frequency << " ";

		        if (includes_positions_) {
		          uint32_t currentIndividualPositions = 0;
		          const uint32_t* curr_positions = index_->curr_list_data()->curr_chunk_decoder().current_positions();
		          uint32_t num_positions = index_->curr_list_data()->GetNumDocProperties();
		          for (size_t i = 0; i < num_positions; ++i) {
		        	//cout << curr_positions[i] << endl;
		        	currentIndividualPositions += curr_positions[i];
		            outputFileHandler << currentIndividualPositions;
		            if (i != (num_positions - 1)){
		            	outputFileHandler << " ";
		            }
		          }
		        }
		        //outputFileHandler << endl;
		        findFlag = true;
	    	}
	    	if(findFlag){
	    		break;
	    	}
	      }
	      outputFileHandler << endl;
	      printf("# of docs involved: %d ", counter);
	      counter = 0;
	    }
	  }
	  //Need to sth reset
  }
  else{
	  cout << "System do NOT recognize the flag value. Exit()" << endl;
	  exit(1);
  }
}

// If 'term' is NULL and 'term_len' is 0, it will go through and output all the lists in the index into a readable format.
// If 'term' is not NULL and 'term_len' is greater than 0, it will find the corresponding list and output this list into a readable format.(One term only)
void IndexCat::Cat(const char* term, int term_len, string flagString, ofstream &outputFileHandler, string queryNumber, string docID, map<string, string> &docLookUpDict, map<string, string> &fileLookUpDict, int &numberOfDocIDNotFound) {
  assert((term == NULL && term_len == 0) || (term != NULL && term_len > 0));

  if (flagString == "3"){
	  if (docLookUpDict.count(docID)>0){

		  vector<string> lineElements;
		  boost::algorithm::split(lineElements, docLookUpDict[docID], boost::algorithm::is_any_of("_") );

		  string key = lineElements[1] + "_" + lineElements[2];
		  string filePath = fileLookUpDict[key];

		  const long beginningPosition = strtol(lineElements[3].c_str(),NULL,0);
		  const long endingPosition = strtol(lineElements[4].c_str(), NULL, 0);



		  // Get collection indexer in order to do indexing on the specific documents.
		  CollectionIndexer& collection_indexer = GetCollectionIndexer();
		  //CollectionIndexer collection_indexer;

		  // Input to the indexer is a list of document collection files we want to index in order.
		  // Deal with the input file.

		  std::istringstream is(filePath + "\n");
		  collection_indexer.ProcessDocumentCollections(is);

		  collection_indexer.showContentOfDocumentForWei(beginningPosition,endingPosition,term,term_len);
		  collection_indexer.EmptyDocumentCollections();
	  }
	  else{
		  cout << "docID:" << docID << " NOT found" << endl;
		  numberOfDocIDNotFound += 1;
	  }


  }
  else{
	  cout << "System do NOT recognize the flag value. Exit()" << endl;
	  exit(1);
  }
}


// The input argument is just the string docID
void IndexCat::Cat(string docID, map<string, string> &docLookUpDict, map<string, string> &fileLookUpDict, string &previousFilePath) {
	  	  cout << "Retrieve and Show the document for docID:" << docID << " on standard output..." << endl;

	  	  bool reloadCompressedFileFlag = true;

	  	  // Get collection indexer in order to do indexing on the specific documents.
	  	  CollectionIndexer& collection_indexer = GetCollectionIndexer();

		  vector<string> lineElements;
		  boost::algorithm::split(lineElements, docLookUpDict[docID], boost::algorithm::is_any_of("_") );
		  //cout << lineElements.size() << endl;
		  string key = lineElements[1] + "_" + lineElements[2];
		  string currentFilePath = fileLookUpDict[key];

		  cout << "docLookUpDict[docID]:" << docLookUpDict[docID] << endl;
		  cout << "key:" << key << endl;
		  cout << "currentFilePath:" << currentFilePath << endl;

		  long beginningPosition = -1;
		  long endingPosition = -1;

		  if(previousFilePath != currentFilePath){
			  collection_indexer.EmptyDocumentCollections();
			  beginningPosition = strtol(lineElements[3].c_str(),NULL,0);
			  endingPosition = strtol(lineElements[4].c_str(), NULL, 0);

			  //CollectionIndexer collection_indexer;

			  // Input to the indexer is a list of document collection files we want to index in order.
			  // Deal with the input file.

			  std::istringstream is(currentFilePath + "\n");
			  collection_indexer.ProcessDocumentCollections(is);
			  collection_indexer.showContentOfDocumentForWei(beginningPosition,endingPosition,docID,reloadCompressedFileFlag);
			  previousFilePath = currentFilePath;
		  }
		  else{
			  reloadCompressedFileFlag = false;
			  beginningPosition = strtol(lineElements[3].c_str(),NULL,0);
			  endingPosition = strtol(lineElements[4].c_str(), NULL, 0);

			  collection_indexer.showContentOfDocumentForWei(beginningPosition,endingPosition,docID,reloadCompressedFileFlag);
		  }
		  cout << "*********************overall statistics begin" << endl;
		  cout << "currentCompressFilePath:" << currentFilePath << endl;
		  cout << "document beginning position byte position:" << beginningPosition << endl;
		  cout << "document ending position byte position:" << endingPosition << endl;
		  cout << "*********************overall statistics end." << endl;
}

// If 'term' is NULL and 'term_len' is 0, it will go through and output all the lists in the index into a readable format.
// If 'term' is not NULL and 'term_len' is greater than 0, it will find the corresponding list and output this list into a readable format.(One term only)
void IndexCat::Cat(string flagString, ofstream &outputFileHandler, vector<string> &queryID_Term_docIDList, string docID, map<string, string> &docLookUpDict, map<string, string> &fileLookUpDict, string &previousFilePath, int &numberOfDocNotFound) {
  if (flagString == "3"){
	  //cout << "Middle docID:" << docID << endl;

	  bool reloadCompressedFileFlag = true;
	  // Get collection indexer in order to do indexing on the specific documents.
	  CollectionIndexer& collection_indexer = GetCollectionIndexer();

	  if (docLookUpDict.count(docID)>0){

		  vector<string> lineElements;
		  boost::algorithm::split(lineElements, docLookUpDict[docID], boost::algorithm::is_any_of("_") );
		  //cout << lineElements.size() << endl;
		  string key = lineElements[1] + "_" + lineElements[2];
		  string currentFilePath = fileLookUpDict[key];


		  if(previousFilePath != currentFilePath){
			  collection_indexer.EmptyDocumentCollections();
			  const long beginningPosition = strtol(lineElements[3].c_str(),NULL,0);
			  const long endingPosition = strtol(lineElements[4].c_str(), NULL, 0);

			  //CollectionIndexer collection_indexer;

			  // Input to the indexer is a list of document collection files we want to index in order.
			  // Deal with the input file.

			  std::istringstream is(currentFilePath + "\n");
			  collection_indexer.ProcessDocumentCollections(is);
			  collection_indexer.showContentOfDocumentForWei(beginningPosition,endingPosition,queryID_Term_docIDList,docID,reloadCompressedFileFlag, outputFileHandler);
			  previousFilePath = currentFilePath;
		  }
		  else{
			  reloadCompressedFileFlag = false;
			  const long beginningPosition = strtol(lineElements[3].c_str(),NULL,0);
			  const long endingPosition = strtol(lineElements[4].c_str(), NULL, 0);

			  collection_indexer.showContentOfDocumentForWei(beginningPosition,endingPosition,queryID_Term_docIDList,docID,reloadCompressedFileFlag, outputFileHandler);
		  }
	  }
	  else{
		  cout << "docID:" << docID << " NOT found" << endl;
		  numberOfDocNotFound ++;
	  }


  }
  else{
	  cout << "System do NOT recognize the flag value. Exit()" << endl;
	  exit(1);
  }
}

CollectionIndexer& IndexCat::GetCollectionIndexer() {
  //static CollectionIndexer collection_indexer;
  static CollectionIndexer collection_indexer;
  return collection_indexer;
}

//This is the IndexCat::~IndexCat desstructor.
IndexCat::~IndexCat() {
  delete index_;
}
